package deibay.server.network.socket;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.logging.Level;

import deibay.DeibayLogger;
import deibay.messages.Message;
import deibay.messages.ReplyLoginMsg;
import deibay.messages.ReplyRegisterMsg;
import deibay.messages.RequestLoginMsg;
import deibay.messages.RequestRegisterMsg;
import deibay.network.IClientCommunicator;
import deibay.server.ServerManager;
import deibay.server.eventargs.NewSessionEventArg;
import deibay.server.eventargs.ReceivedMessageEventArg;
import deibay.server.events.CreateSessionEvent;
import deibay.server.events.ReceivedMessageEvent;
import deibay.users.User;
import deibay.util.ParserXML;


/**
 * @author   Joel Cordeiro
 */
public class SocketClientCommunicator implements IClientCommunicator, Runnable{

	private Socket socket;		
	private DataInputStream in;
	private DataOutputStream out;	
	/**
	 * @uml.property  name="address"
	 */
	private InetAddress address;	
	private boolean keepAlive;		
	private User user = null;
	
	public SocketClientCommunicator(Socket socket)
	{		
		 try {						
			this.socket = socket;
		    address = this.socket.getInetAddress();
			keepAlive = true;
			in = new DataInputStream(this.socket.getInputStream());
			out = new DataOutputStream(this.socket.getOutputStream());
		} catch (IOException e) {
			DeibayLogger.getInstance().getLog().log(Level.SEVERE, "Erro ao criar SocketClientCommunicator.\n");			
		}		 
	}		

	/**
	 * @return
	 * @uml.property  name="address"
	 */
	public InetAddress getAddress() {		
		return this.address;
	}

	public void run() {															
		//a espera de uma mensagem de login ou registo
		while(user==null)
		{
			user = authenticate();	
		}							
		//espera por novas mensagens
		while (keepAlive == true) {					
			Message message = receive();	
			
			if (message == null)
			{
				close();				
				return;
			}
			else putMessageToQueue(message);
		}
	}
			
	private void putMessageToQueue(Message message) {		
		ServerManager.getInstance().getEventManager().fireEvent(new ReceivedMessageEvent(new ReceivedMessageEventArg(this.user, message)));
	}

	public Message receive()
	{
		try {			
			return ParserXML.getInstance().parseXMLToMessage(in.readUTF());
		} catch (IOException e) {				
			DeibayLogger.getInstance().getLog().log(Level.SEVERE, "User "+user+" perdeu a sua ligacao.\n");
		}		
		
		return null;
	}

	private synchronized User authenticate() {			
		Message message = this.receive();
		if (message instanceof RequestLoginMsg) {
			DeibayLogger.getInstance().getLog().log(Level.INFO, "Cliente a tentar logar-se..\n");			
			return login((RequestLoginMsg)message);
		} else if (message instanceof RequestRegisterMsg) {
			DeibayLogger.getInstance().getLog().log(Level.INFO, "Cliente a tentar registar-se..\n");
			return register((RequestRegisterMsg)message);
		}		
		return user;
	}	
	
	private User login(RequestLoginMsg msg)
	{		
		RequestLoginMsg temp = (RequestLoginMsg)msg;
		System.err.println("login: "+temp.getLogin()+" pass: "+temp.getPassword());
		user = ServerManager.getInstance().authenticate(temp.getLogin(), temp.getPassword());
		
		if(user == null)
		{
			send(new ReplyLoginMsg(ReplyLoginMsg.class.getSimpleName(), false, ""));
			return null;
		}
		else 
		{			
			ServerManager.getInstance().getEventManager().fireEvent(new CreateSessionEvent(new NewSessionEventArg(this, user)));
			if(user.isBidder())
				send(new ReplyLoginMsg(ReplyLoginMsg.class.getSimpleName(), true, "bidder"));
			else if(user.isSeller())
				send(new ReplyLoginMsg(ReplyLoginMsg.class.getSimpleName(), true, "seller"));
			return user;
		}
	}
	
	private User register(RequestRegisterMsg msg)
	{		
		RequestRegisterMsg register = (RequestRegisterMsg)msg;
		user = ServerManager.getInstance().register(register.getLogin(), register.getPassword(), register.getEmail(), register.getAddress(), register.getRole());
		if(user == null)
		{
			send(new ReplyRegisterMsg(ReplyRegisterMsg.class.getSimpleName(), false, ""));
			return null;
		}
		else
		{			
			ServerManager.getInstance().getEventManager().fireEvent(new CreateSessionEvent(new NewSessionEventArg(this, user)));
			if(user.isBidder())
				send(new ReplyRegisterMsg(ReplyLoginMsg.class.getSimpleName(), true, "bidder"));
			else if(user.isSeller())
				send(new ReplyRegisterMsg(ReplyLoginMsg.class.getSimpleName(), true, "seller"));
			return user;
		}
	}
	
	public void send(Message message)
	{
		try {
			out.writeUTF(ParserXML.getInstance().parseMessageToXML(message));
			out.flush();
		} catch (IOException e) {
			DeibayLogger.getInstance().getLog().log(Level.SEVERE, "User "+user+" perdeu a sua ligacao.\n");
		}				
	}	
	
	public void close()
	{
		keepAlive = false;
		synchronized(socket)
		{
			try {
				in.close();
				out.close();
				this.socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
		
}
